package comm;

import sim.Sim;
import comm.Message.MessageType;
import env.*;

/**
 * Handles sending of messages.
 */
public class MessageManager {
	// Set to less than 1.0 to simulate message failure
	public static final double MESSAGE_SUCCESS_RATE = 1;

	// Sent when robot has arrived at pushable object
	public static void sendArrive(String from, PushableObj o) {
		Message m = new Message(MessageType.ARRIVE, from, new Object[] { o.getID() });
		sendToAll(m, from);
	}

	// Sent when waiting robot receives arrive message
	public static void sendConfirm(String from, String to) {
		Message m = new Message(MessageType.CONFIRM, from, new Object[] {});
		sendTo(m, to);
	}

	// Sends message containing own cost function computation of pushable object
	public static void sendCost(String from, PushableObj o, double cost) {
		Message m = new Message(MessageType.COST, from, new Object[] { o.getID(), cost });
		sendToAllIncludingSelf(m, from);
	}

	// Sends message requesting cost function values of other robots
	public static void sendCostRequest(String from, PushableObj o) {
		Message m = new Message(MessageType.COST_REQUEST, new Object[] { o.getID() });
		sendToAll(m, from);
	}

	// For guiding helper robot
	public static void sendForcePoint(String from, String to, int fp) {
		Message m = new Message(MessageType.FORCE_POINT, from, new Object[] { fp });
		sendTo(m, to);
	}

	// For something really fancy
	public static void sendSetVelocity(String from, String to, double vel) {
		Message m = new Message(MessageType.SET_VEL, from, new Object[] { vel });
		sendTo(m, to);
	}

	public static void sendVelocity(String from, String to, PushableObj o, double vel) {
		Message m = new Message(MessageType.VEL, from, new Object[] { o.f2, vel });
		sendTo(m, to);
	}
	
	// Sends message to inform Robot 'to' that Robot 'from' is on its own
	public static void sendOnTheWay(String from, String to, int numofsteps){
		Message m = new Message(MessageType.ONTHEWAY, from, new Object[] {numofsteps});
		System.out.println(from + " is on its way to help " + to + ". ETA: " + numofsteps);
		sendTo(m, to);
	}
	
	// Sends message to ask the progress of the Robot on its way to help push the object
	public static void sendCheckUp(String from, String to){
		Message m = new Message(MessageType.CHECKUP, from, new Object[] {5});
		System.out.println(from + " is checking up on " + to); 
		sendTo(m, to);
	}

	// Send to all robots except sender
	private static void sendToAll(Message m, String from) {
		for (Obj obj : Sim.a)
			if (obj instanceof Robot && !obj.getID().equals(from))
				receive((Robot) obj, m);
	}
	
	// Send to all robots including sender
	private static void sendToAllIncludingSelf(Message m, String from){
		for (Obj obj : Sim.a){
			if (obj instanceof Robot)
				receive((Robot) obj, m);
		}
	}

	// Send to one recipient
	private static void sendTo(Message m, String to) {
		for (Obj obj : Sim.a)
			if (obj.getID().equals(to)) {
				receive((Robot) obj, m);
				return;
			}
	}
	
	// Receive message 
	private static void receive(Robot r, Message m) {
		if (Math.random() <= MESSAGE_SUCCESS_RATE)
			r.receiveMessage(m);
		else
			Sim.err("Message lost: " + m);
	}
	
	// Send to one recipient with coordinates
	private static void sendTo(Message m, String to, double x, double y) {
		for (Obj obj : Sim.a)
			if (obj.getID().equals(to)) {
				receive((Robot) obj, m, x, y);
				return;
			}
	}

	// Receive message with coordinates
	private static void receive(Robot r, Message m, double x, double y) {
		if (Math.random() <= MESSAGE_SUCCESS_RATE)
			r.receiveMessage(m);
		else
			Sim.err("Message lost: " + m);
	}
	
	
	public static void sendCoordinates(String from, String to, double x, double y)
	{
		Message m = new Message(MessageType.CHECKUP,from,x,y);
		sendTo(m, to, x, y);
	}

}
